#include "Application.h"

using namespace Ogre;

Application::Application()
{
}

void Application::go()
{
    createRoot();
    defineResources();
    setupRenderSystem();
    createRenderWindow();
    initializeResourceGroups();
    setupScene();
    setupInputSystem();
    setupCEGUI();
    createFrameListener();
    startRenderLoop();
}

void Application::createRoot()
{
	mRoot = new Root();
}

void Application::defineResources()
{
	String secName, typeName, archName;
	ConfigFile cf;
	cf.load("resources.cfg");

	ConfigFile::SectionIterator seci = cf.getSectionIterator();
	while (seci.hasMoreElements())
	{
		secName = seci.peekNextKey();
		ConfigFile::SettingsMultiMap *settings = seci.getNext();
		ConfigFile::SettingsMultiMap::iterator i;
		for (i = settings->begin(); i != settings->end(); ++i)
		{
			typeName = i->first;
			archName = i->second;
			ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);
		}
	}
}

void Application::setupRenderSystem()
{
	if(!mRoot->restoreConfig() && !mRoot->showConfigDialog())
		throw Exception(52, "User canceled the config dialog!", "Application::setupRenderSystem()");
}

void Application::createRenderWindow()
{
	mRoot->initialise(true, "Knight");
}

void Application::initializeResourceGroups()
{
	TextureManager::getSingleton().setDefaultNumMipmaps(5);
	ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
}

void Application::setupScene()
{
	SceneManager *mgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE, "Default SceneManager");
	Camera *cam = mgr->createCamera("Camera");
	cam->setNearClipDistance(5);
	Viewport *vp = mRoot->getAutoCreatedWindow()->addViewport(cam);

	mgr->setAmbientLight(ColourValue(1.0f, 1.0f, 1.0f));
	mgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);

	mEnt = mgr->createEntity("knight", "Knight.mesh");
	mEnt->setCastShadows(true);

	mAnimationState = mEnt->getAnimationState("idle");
	mAnimationState->setLoop(true);
	mAnimationState->setEnabled(true);


	knightNode = mgr->getRootSceneNode()->createChildSceneNode("knightNode");
	knightNode->attachObject(mEnt);
	knightNode->pitch(Degree(90));
	knightNode->roll(Degree(-90));

	Plane plane(Vector3::UNIT_Y, 0);

	MeshManager::getSingleton().createPlane("ground",
       ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,
       1500, 1500, 20, 20, true, 1, 5, 5, Vector3::UNIT_Z);

	Entity* ent = mgr->createEntity("GroundEntity", "ground");
	mgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);
	ent->setMaterialName("Examples/Rockwall");
	ent->setCastShadows(false);

	Plane skyPlane;
    skyPlane.d = 1000;
    skyPlane.normal = Vector3::UNIT_Z;

	mgr->setSkyPlane(true, skyPlane, "KnightEnv/Bg", 1024, 128);

	Light *light = mgr->createLight("Light1");
    light->setType(Light::LT_POINT);
    light->setPosition(Vector3(250, 150, -200));
	light->setDiffuseColour(ColourValue::White);
    light->setSpecularColour(ColourValue::White);

	SceneNode* node = mgr->getRootSceneNode()->createChildSceneNode("camNode1", Vector3(0, 50, 800));
	node->attachObject(cam);
}

void Application::setupInputSystem()
{
	size_t windowHnd = 0;
	std::ostringstream windowHndStr;
	OIS::ParamList pl;
	RenderWindow *win = mRoot->getAutoCreatedWindow();

	win->getCustomAttribute("WINDOW", &windowHnd);
	windowHndStr << windowHnd;
	pl.insert(std::make_pair(std::string("WINDOW"), windowHndStr.str()));
	mInputManager = OIS::InputManager::createInputSystem(pl);

	try
	{
		mKeyboard = static_cast<OIS::Keyboard*>(mInputManager->createInputObject(OIS::OISKeyboard, true));
	}
	catch (const OIS::Exception &e)
	{
		throw Exception(42, e.eText, "Application::setupInputSystem");
	}
}

void Application::setupCEGUI()
{
	SceneManager *mgr = mRoot->getSceneManager("Default SceneManager");
	RenderWindow *win = mRoot->getAutoCreatedWindow();

	// CEGUI setup
	mRenderer = new CEGUI::OgreCEGUIRenderer(win, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mgr);
	mSystem = new CEGUI::System(mRenderer);
}

void Application::createFrameListener()
{
	mListener = new ExitListener(mKeyboard);
	mAnimationListener = new AnimationListener(mAnimationState, mKeyboard, mEnt, knightNode);
	mRoot->addFrameListener(mListener);
	mRoot->addFrameListener(mAnimationListener);
}

void Application::startRenderLoop()
{
	mRoot->startRendering();
}

Application::~Application()
{
	mInputManager->destroyInputObject(mKeyboard);
	OIS::InputManager::destroyInputSystem(mInputManager);

	delete mRenderer;
	delete mSystem;

	delete mListener;
	delete mAnimationListener;
	delete mRoot;
}
